package roborally.board;

import roborally.itemsonboard.BoardItem;
import roborally.itemsonboard.EnergyItem;
import roborally.supporting.PhysicalAmount;
import roborally.supporting.Position;
import roborally.supporting.Unit;

/**
 * A class, collection some defaultOptions for selecting a particular subrange of items of a board.
 * 
 * @author Molderez Tom, 2de bach Ingenieurswetenschappen, Computerwetenschappen-Elektrotechniek and Reniers Jorn, 2de bach Ingenieurswetenschappen Werktuigkunde-Computerwetenschappen
 * @version 3.0
 *
 */
public class Option {
	
	/**
	 * Returns a SelectionOption which only allows boardItems which extends EnergyItem and
	 * have at least the given physicalAmount as energy.
	 * 
	 * @param physicalAmount The minimal energy the boardItems must have.
	 * @pre The unit of the physicalAmount must be convertible to Unit.WATTSECOND
	 * 		| physicalAmount.getUnit().canConvertTo(Unit.WATTSECOND)
	 * @return
	 */
	public static SelectionOption minimalEnergy(final PhysicalAmount physicalAmount) {
		assert physicalAmount.getUnit().canConvertTo(Unit.WATTSECOND);
		return new SelectionOption() {

			/**
			 * Controls if the given boardItem satisfies some predefined option.
			 * 
			 * @param boardItem The boardItem to control.
			 * @return False if the boardItem is null
			 * 		| if boardItem == null
			 * 		|	then result == false
			 * @return False if the boardItem is not a sub type of EnergyItem
			 * 		| if !EnergyItem.class.isInstance(BoardItem)
			 * 		|	then result == false
			 * @return False if boardItem does not have the necessary energy
			 * 		| if physicalAmount.compareTo(EnergyItem.class.cast(boardItem).getEnergyLevel()) > 0
			 * 		|	then result == false
			 * @return True in any other case.
			 * 		| result == true
			 */
			@Override
			public boolean satisfy(BoardItem boardItem) {
				if(boardItem == null)
					return false;
				else if(!EnergyItem.class.isInstance(boardItem))
					return false;
				else if(physicalAmount.compareTo(EnergyItem.class.cast(boardItem).getEnergyLevel()) > 0)
					return false;
				else
					return true;
			}
			
		};
	}
	
	/**
	 * Returns a SelectionOption which only allows boardItems that are located on a position
	 * smaller then the given one.
	 * 
	 * @param smallest The smallest position of an item that will be accepted.
	 * @param largest The largest position of an item that will be accepted
	 * @pre
	 * 		| smallest != null
	 * @pre 
	 * 		| largest != null
	 * @return
	 */
	public static SelectionOption subRange(final Position smallest, final Position largest) {
		assert smallest != null;
		assert largest != null;
		return new SelectionOption() {
			
			@Override
			public boolean satisfy(BoardItem boardItem) {
				if(boardItem == null)
					return false;
				else if(boardItem.getPosition().getX() >= smallest.getX() && 
						boardItem.getPosition().getX() <= largest.getX() &&
						boardItem.getPosition().getY() >= smallest.getY() &&
						boardItem.getPosition().getY() <= largest.getY() )
					return true;
				else
					return false;
			}
		};
	}
	
	/**
	 * Every boardItem will be accepted by the SelectionOption that will be returned.
	 * 
	 * @return
	 */
	public static SelectionOption none() {
		return new SelectionOption() {
			
			@Override
			public boolean satisfy(BoardItem boardItem) {
				return true;
			}
		};
	}
	
	/**
	 * No object can be created of this class.
	 */
	private Option() {
	}
	
	//TODO: waarom al deze moeite???? om een hele klasse option te schrijven?????
	// ge moet toch gewoon als parameter voor de iterator een methode meegeven, die oproepen op een BoardItem en da resultaat gebruiken???
	// Zie ook Toledo verduidelijking: project Dogs and Paintings, methode getTotalOf in Person.
	// Dit is wat aanpasbaarheid betreft slecht.
	// Als er nu nieuwe dingen/methodes bijkomen die eveneens een conditie zouden kunnen bepalen, dan moeten we hier ook nieuwe 
	// dingen zitten toevoegen !!!!!
	
	//Ik doe juist hetzelfde, maar in plaats van een methode te gebruiken, gebruik ik een object met
	//slechts 1 methode. In deze klasse staan voorbeeld options. Je kan desnoods bij aanroep van de methode zelf een
	//anoniem object meegeven met de specifieke wensen.
	//voordeel ten opzichte van een methode:
	//controle of de methode de juiste vorm heeft (returntype, argumenttype, ...) valt weg.
	//Alle mogelijke opties zitten in 1 klasse verzameld (deze is verantwoordelijk voor het beheer
	//van opties, inkapseling)
	//De code is veel eenvoudiger dan het gebruik van methoden als argument.

}
